/** 
 * @(#)NodeRIP.java
 */

/**
 * @author Didier Gutacker
 * @version 1.1
 * @see Knoten 
 * @see RIPRoutingTable
 * @see PeriodicSenderRIP 
 * @see PeriodicUpdateSender
 * @see NetworkRIP
 * @see LinkRIP
 *
 */

import java.awt.*;
import java.util.Vector;

public class NodeRIP extends Knoten {
  private RIPRoutingTable routingTable;

  private PeriodicSenderRIP sender;
  private PeriodicUpdateSender updateSender;
  private boolean sending = false;
  private boolean sendingupdate = false;
 
  public NodeRIP( int id, NetworkRIP net, Graphics gc, int xpos, int ypos, int width, int height ) {
    super( id, (SimulationObject) net, gc, xpos, ypos, width, height );
    if ( ! net.isFinished() ) {
      net.writeln( text.nodeTitleText + " " + id + " " + text.addedText );
    } // if
    routingTable = new RIPRoutingTable( id );
    tracker = new MediaTracker( net.getCanvas() );
    tracker.addImage( nodeImage, 1 );
    try{ 
      tracker.waitForID( 1 ); 
    } catch(InterruptedException e){
    } // try;
    if( tracker.isErrorID(1) ){
      System.err.println( "Error:  could not load \"router.gif\".");
    } // if 
  } // NodeRIP.NodeRIP
  
  /**
   * Weckt die zuvor mit suspend() schlafengelegten Thread wieder auf
   *
   * @see NodeRIP#suspend
   */
  
  public void resume() {
    super.resume();
    if ( sender != null ) {
      sender.resume();
    } // if
    if ( updateSender != null ) {
      updateSender.resume();
    } // if
  } // ArchitectureComponent.resume

  /**
   * Legt den objekteigenen Thread und den eventuell existierenden Sender schlafen
   *
   * @see NodeRIP#resume
   */

  public void suspend() {
    if ( sender != null ) {
      sender.suspend();
    } // if
    if ( updateSender != null ) {
      updateSender.suspend();
    } // if
    super.suspend();
  } // ArchitectureComponent.suspend

  public void sendDataPackets(int to) {
    RoutingEntry nodeEntry = routingTable.lookUp( to );
    ((LinkRIP)nodeEntry.l).sendDataPacket( this.id, this,to );
  } // NodeRIP.sendDataPackets

  public void finishUp() {
    ((NetworkRIP)net).writeln( text.nodeTitleText + " " + id + " " + text.removedText );
    attachedLinksGuard.acquire();
    for ( int i = attachedLinks.size() - 1; ; i = attachedLinks.size() - 1) { // trick:  disconnect will remove links
      if ( i < 0 ) {
	break;
      } // if
     ((NetworkRIP) net).removeLink( ( (LinkRIP) attachedLinks.elementAt( i ) ) );
    } // for
    attachedLinks = null;
    attachedLinksGuard.release();
    super.finishUp();
    routingTable = null;
    nodeImage = null;
    fontMetrics = null;
    if ( sender != null ) {
      stopSending();
      sender.finishUp();
      sender = null;
    } // if
    if ( updateSender != null ) {
      stopUpdateSender();
      updateSender.finishUp();
      updateSender = null;
    } // if   
  } // NodeRIP.finishUp

  synchronized public void paint() {
    if ( finished ) {
      return;
    } // if
    while ( !initialized ) {
      Thread.yield();
    } // while

    absXPos = xpos + net.getXOffset();
    absYPos = ypos + net.getYOffset();
    gc.drawImage( nodeImage, absXPos, absYPos + textHeight, width, height - textHeight, this );
    if ( marked ) {
      gc.setColor( Color.red );
      gc.drawRect( absXPos, absYPos, width - 1, height - 1 );
    } else if ( sending ) {
      gc.setColor( Color.magenta );
    } else {
      gc.setColor( Color.black );
    } // if
    String t = text.nodeTitleText + " " + id;
    gc.drawString( t, absXPos + ( ( width - fontMetrics.stringWidth( t ) )
					/ 2 ), absYPos + fontMetrics.getAscent() );
  } // NodeRIP.paint

  public void connect( int nodeID, int metric, LinkRIP l ) {
    if ( ! net.isFinished() ) {
      ((NetworkRIP)net).acquireTextLock();
     ((NetworkRIP)net).write( text.nodeTitleText + " " + id + " " + text.nodeConnectText + " " + text.linkTitleText +" " + l.getID() + " " );
      if ( attachedLinks.size() > 0 ) {
	((NetworkRIP)net).writeln( text.nodeConnectAdditionalText1 + " " +
		     text.routingUpdatePacketText + " " +
		     text.andText + " " + text.aText + " " +
		     text.routingTableRequestPacketText + " " +
		     text.nodeConnectAdditionalText2 );
      } else {
	((NetworkRIP)net).writeln( "" );
      } // if
      ((NetworkRIP)net).releaseTextLock();
    } // if
    int infinity = ((NetworkRIP)net).getInfinity();
    Vector v = new Vector();
    RoutingEntry entry = new RoutingEntry( id, 0, l, infinity );
    v.addElement( entry );
    l.sendRoutingTableUpdate( this, v );
    l.sendRoutingTableRequest( this );
    v = new Vector();
    entry = new RoutingEntry( nodeID, metric, l, infinity );
    v.addElement( entry );
    attachedLinksGuard.acquire();
    for ( int i = 0; i < attachedLinks.size(); i += 1 ) {
      ( (LinkRIP) attachedLinks.elementAt( i ) ).sendRoutingTableUpdate( this, v );
      ( (LinkRIP) attachedLinks.elementAt( i ) ).sendRoutingTableRequest( this );
    } // for
    attachedLinks.addElement( l );
    attachedLinksGuard.release();
    if ( updateSender == null ) {
      startUpdateSender();
    } // if  
  } // NodeRIP.connect

  public void disconnect( LinkRIP l ) {
    int infinity = ((NetworkRIP)net).getInfinity(); 
    attachedLinksGuard.acquire();
    routingTable.deregisterLink( l, infinity );    
    attachedLinks.removeElement( l );
    if ( ! net.isFinished() ) {
      for ( int i = 0; i < attachedLinks.size(); i += 1 ) {
	sendRoutingEntries( (LinkRIP) attachedLinks.elementAt( i ) );
	( (LinkRIP) attachedLinks.elementAt( i ) ).sendRoutingTableRequest( this );
      } // for
    } // if
    attachedLinksGuard.release();
  } // NodeRIP.disconnect

  public void processRoutingTableUpdate( LinkRIP l, Vector entries ) {
    if ( finished ) {
      return;
    } // if
    Vector updatedEntries = new Vector();
    Vector poisonedUpdatedEntries = new Vector();
    boolean routingTableRequested = false;
    for ( int i = 0; i < entries.size(); i += 1 ) {
      RoutingEntry entry = (RoutingEntry) entries.elementAt( i );
      int metric = entry.metric;
      int infinity = entry.infinity;
      int nodeID = entry.destinationID;
      if ( ! net.isFinished() ) {
	((NetworkRIP)net).acquireTextLock();
	((NetworkRIP)net).write( text.nodeTitleText + " " + id + " " + text.processRoutingTableUpdateText + " " + text.nodeTitleText + " " + nodeID + " " + text.viaText + " " + text.linkTitleText + " " + l.getID() + " " + text.withMetricText + " " );
	if ( metric == infinity ) {
	  ((NetworkRIP)net).writeln( "\"infinity\"" );
	} else if ( metric > infinity ) {
	  ((NetworkRIP)net).writeln( "\"infinity\" + " + ( metric - infinity + l.getWeight() ) + "\n + (poison reverse)");
	} else {
	  ((NetworkRIP)net).writeln( "" + ( metric + l.getWeight() ) );
	} // if 
	((NetworkRIP)net).releaseTextLock();
      } // if

      if ( nodeID != id ) {
	if ( routingTable.update( l, nodeID, metric, (LinkRIP) entry.l, infinity ) ) {
	  if ( ! net.isFinished() ) {
	    ((NetworkRIP)net).acquireTextLock();
	    ((NetworkRIP)net).write( text.nodeTitleText + " " + id + " " +
		       text.routingTableUpdateText + " " );
	    if ( attachedLinks.size() > 1 ) {
	      ((NetworkRIP)net).writeln( text.nodeConnectAdditionalText1 + " " +
			   text.routingUpdatePacketText + " " +
			   text.nodeConnectAdditionalText2 );
	    } else {
	      ((NetworkRIP)net).writeln( "" );
	    } // if
	    ((NetworkRIP)net).releaseTextLock();
	  } // if
	  RoutingEntry updatedEntry = routingTable.lookUp( nodeID );
	  if ( updatedEntry.infinity == updatedEntry.metric ) {
	    routingTableRequested = true;
	  } // if
	  RoutingEntry newEntry = new RoutingEntry( updatedEntry.destinationID, updatedEntry.metric, updatedEntry.l, updatedEntry.infinity );
	  updatedEntries.addElement( newEntry );    
	  RoutingEntry poisonedEntry = new RoutingEntry( updatedEntry.destinationID, updatedEntry.metric + updatedEntry.infinity, updatedEntry.l, updatedEntry.infinity );
	  poisonedUpdatedEntries.addElement( poisonedEntry );
	} // if
      } else {
	if ( ! net.isFinished() ) {
	  ((NetworkRIP)net).writeln( text.nodeTitleText + " " + id + " " + text.nodeThrowAwayPacketText + " " +text.routingUpdatePacketText + " " );
	} // if
      } // if
    } // for
    if ( !poisonedUpdatedEntries.isEmpty() ) {
      l.sendRoutingTableUpdate( this, poisonedUpdatedEntries );
    } // if
    if ( !updatedEntries.isEmpty() ) {
      attachedLinksGuard.acquire();
      for ( int i = 0; i < attachedLinks.size(); i += 1 ) {
	if ( !( (LinkRIP) attachedLinks.elementAt( i ) ).equals( l ) ) {
	  ( (LinkRIP) attachedLinks.elementAt( i ) ).sendRoutingTableUpdate( this, updatedEntries );
	} // if
	if ( routingTableRequested ) {
	  ( (LinkRIP) attachedLinks.elementAt( i ) ).sendRoutingTableRequest( this );
	} // if
      } // for
      attachedLinksGuard.release();
    } // if
  } // NodeRIP.processRoutingTableUpdate

  public void sendRoutingEntries( LinkRIP l ) {
    if ( finished ) {
      return;
    } // if

    Vector entries = routingTable.getEntries();
    Vector outputEntries = new Vector();

    for ( int i = 0; i < entries.size(); i += 1 ) {
      RoutingEntry entry = (RoutingEntry) entries.elementAt( i );

      // split horizon processing
      int infinity = ((NetworkRIP)net).getInfinity();
      if ( entry.l.equals( l ) ) {
	outputEntries.addElement( new RoutingEntry( entry.destinationID, infinity + entry.metric, entry.l, infinity ) );
      } else {
	outputEntries.addElement( new RoutingEntry( entry.destinationID, entry.metric, entry.l, infinity ) );
      } // if
    } // for
    if ( ! outputEntries.isEmpty() ) { 
      l.sendRoutingTableUpdate( this, outputEntries );
    } // if
  } // NodeRIP.sendRoutingEntries

  public final void forwardPacket( int nodeID, LinkRIP l, int toID ) {
    if ( this.id == nodeID ) {
      return;
    } // if
    if ( ! l.equals( routingTable.lookUp( nodeID ).l ) ) { // forward from shortest path only
      return;
    } // if
    Vector links = routingTable.getForwardInterfaces( toID );
    if ( links != null ) {
      for ( int i = 0; i < links.size(); i += 1 ) {
	((LinkRIP) links.elementAt( i )).sendDataPacket( nodeID, this, toID );
      } // for
    } // if
  } // NodeRIP.forwardPacket 

  synchronized public void startUpdateSender() {
    if ( ! sendingupdate ) {
      updateSender = new PeriodicUpdateSender( this );
      sendingupdate = true;
    } // if
  } // NodeRIP.startUpdateSender

  synchronized public void stopUpdateSender() {
    if ( sendingupdate ) {
      sendingupdate = false;
      updateSender.stop();
    } // if
   }
     
  synchronized public void startSending(int to) {
    if ( !sending ) {
      sender = new PeriodicSenderRIP( this,to );
      sending = true;
    } // if
  } // NodeRIP.startSending

  synchronized public void stopSending() {
    if ( sending ) {
      sending = false;
      sender.stop();
      paint();
    } // if
  } // NodeRIP.stopSending

  public void inspectTable() {
    routingTable.createTableWindow( this, (NetworkRIP)net );
  } // NodeRIP.inspectTable

  public void sendUpdatePackets() {
    if ( !(((NetworkRIP)net).noPeriodicUpdates()) ) {
      attachedLinksGuard.acquire(); 
      for ( int i = 0 ; i < attachedLinks.size() ; i++ ) { 
        sendRoutingEntries( (LinkRIP ) attachedLinks.elementAt(i) );
      } // for
      attachedLinksGuard.release();
    } // if
  } // NodeRIP.sendUpdatePackets 

} // NodeRIP







